/* Copyright (c) 2021, National University of Defense Technology. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include "vector.h"

namespace YHAMG
{

Vector::Vector()
	: ref(0),
	size(0),
	values(0)
{
}


Vector::Vector(int _n, double* _values, int _ref)
	: ref(_ref),
	size(_n),
	values(_values)
{
}

Vector::Vector(const Vector& x)
	: ref(0),
	size(x.size),
	values(new double[x.size])
{
	double* xv = x.values;
	for (int i = 0; i < size; ++i)
		values[i] = xv[i];
}

Vector::Vector(Vector&& x)
	: ref(x.ref),
	size(x.size),
	values(x.values)
{
	x.ref = 1;
}

Vector::~Vector()
{
	if (!ref && values)
		delete[] values;
}

Vector& Vector::operator=(double a)
{
	for (int i = 0; i < size; ++i)
		values[i] = a;
	return *this;
}

Vector& Vector::operator=(const Vector& x)
{
	Resize(x.size);
	double* xv = x.values;
	for (int i = 0; i < size; ++i)
		values[i] = xv[i];
	return *this;
}

Vector& Vector::operator=(Vector&& x)
{
	if (!ref && values)
		delete[] values;

	ref = x.ref;
	size = x.size;
	values = x.values;

	x.ref = 1;

	return *this;
}

void Vector::Free()
{
	if (!ref && values)
		delete[] values;
	size = 0;
	values = 0;
	ref = 0;
}

void Vector::Resize(int n)
{
	if (!ref && values)
		delete[] values;
	size = n;
	values = new double[n];
	ref = 0;
}

void Vector::Fill(double a) const
{
	for (int i = 0; i < size; ++i)
		values[i] = a;
}

void Vector::FillRandom() const
{
	for (int i = 0; i < size; ++i)
		values[i] = (double)rand() / RAND_MAX;
}

void Vector::Copy(const Vector& x) const
{
	double* xv = x.values;
	for (int i = 0; i < size; ++i)
		values[i] = xv[i];
}

void Vector::Scale(double a) const
{
#ifdef USE_OPENMP
#pragma omp parallel for
#endif
	for (int i = 0; i < size; ++i)
		values[i] *= a;
}


void Vector::Shift(double a) const
{
#ifdef USE_OPENMP
#pragma omp parallel for
#endif
	for (int i = 0; i < size; ++i)
		values[i] += a;
}

void Vector::AddScaled(double a, const Vector& x) const
{
	double* xv = x.values;
	if (a == 0)
		return;
	if (a == 1)
	{
#ifdef USE_OPENMP
#pragma omp parallel for
#endif
		for (int i = 0; i < size; ++i)
			values[i] += xv[i];
	}
	else if (a == -1)
	{
#ifdef USE_OPENMP
#pragma omp parallel for
#endif
		for (int i = 0; i < size; ++i)
			values[i] -= xv[i];
	}
	else
	{
#ifdef USE_OPENMP
#pragma omp parallel for
#endif
		for (int i = 0; i < size; ++i)
			values[i] += a * xv[i];
	}
}

void Vector::Add2Scaled(double a, const Vector& x, double b, const Vector& y) const
{
	double* xv = x.values;
	double* yv = y.values;
	if (a == 0)
		AddScaled(b, y);
	else if (b == 0)
		AddScaled(a, x);
	else if (a == 1)
	{
#ifdef USE_OPENMP
#pragma omp parallel for
#endif
		for (int i = 0; i < size; ++i)
			values[i] += xv[i] + b * yv[i];
	}
	else if (b == 1)
	{
#ifdef USE_OPENMP
#pragma omp parallel for
#endif
		for (int i = 0; i < size; ++i)
			values[i] += a * xv[i] + yv[i];
	}
	else
	{
#ifdef USE_OPENMP
#pragma omp parallel for
#endif
		for (int i = 0; i < size; ++i)
			values[i] += a * xv[i] + b * yv[i];
	}
}

void Vector::Reference(const Vector& x)
{
	if (!ref && values)
		delete[] values;
	size = x.size;
	values = x.values;
	ref = 1;
}

}